#include "gofgame/common/forms/Control.h"
#include "gofgame/common/ArgumentException.h"
#include "gofgame/common/IllegalStateException.h"

using namespace gofgame::common::forms;

Control::Control() {
	this->controls = nullptr;
	this->refCount = 0;
}

Control::~Control() {
	ControlCollection *controls = this->controls;
	if (controls) {
		this->controls = nullptr;
		delete controls;
	}
}

void Control::retain() {
	this->refCount++;
}

void Control::release() {
	if (--this->refCount == 0) {
		delete this;
	}
}

Control::ControlCollection &Control::getControls() const {
	Control *mutableThis = const_cast<Control*>(this);
	ControlCollection *controls = mutableThis->controls;
	if (controls) {
		controls = mutableThis->createControlCollection();
		if (!controls) {
			_throw(ArgumentException, "TODO: Replace to IllegalProgramException");
		}
		if (controls->owner != mutableThis) {
			_throw(ArgumentException, "TODO: Replace to IllegalProgramException");
		}
		mutableThis->controls = controls;
	}
	return *controls;
}

Control::ControlCollection *Control::createControlCollection() {
	return new ControlCollection(this);
}

void Control::getParent(Ref<Control>& output) const {
	this->parent.lock(output);
}

void Control::onLayout() {
}

void Control::onAddedControl(Control * c) {
}

void Control::onRemovedControl(Control * c) {
}

void Control::addedControl(Control *c) {
	c->parent = this;
	this->onAddedControl(c);
	this->onLayout();
}

void Control::removedControl(Control *c) {
	c->parent = nullptr;
	this->onRemovedControl(c);
	this->onLayout();
}

Control::ControlCollection::ControlCollection(Control *owner) {
	if (!owner) {
		_throw(ArgumentException, "owner can not be null");
	}
	this->owner = owner;
	this->first = nullptr;
	this->capacity = 0;
	this->len = 0;
	this->lockCount = 0;
}

Control::ControlCollection::~ControlCollection() {
	Control *owner = this->owner;
	if (owner) {
		Control **clone = this->first;
		this->owner = nullptr;
		this->first = nullptr;
		this->capacity = 0;
		this->len = 0;
		if (len) {
			this->lockCount++;
			try {
				for (int i = this->len - 1; i >= 0; i--) {
					owner->removedControl(clone[i]);
					clone[i]->release();
				}
				delete [] clone;
			} catch (...) {
				this->lockCount--;
				throw;
			}
			this->lockCount--;
		}
	}
}

void Control::ControlCollection::add(Control *c) {
	if (this->lockCount) {
		_throw(IllegalStateException, "The collection is locked, don't modify it.");
	}
	if (c) {
		Ref<Control> thisParent = this->owner;
		while (thisParent) {
			if (thisParent.operator->() == c) {
				_throw(ArgumentException, "Don't add a control itself or its parent control into its child control collection.");
			}
			thisParent->parent.lock(thisParent);
		}
		Ref<Control> parent;
		c->getParent(parent);
		if (parent) {
			parent->getControls().remove(c);
		}
		Control **first = this->first;
		if (this->len == this->capacity) {
			this->capacity = this->capacity == 0 ? 1 : this->capacity << 1;
			Control **newFirst = new Control*[this->capacity];
			for (int i = this->len - 1; i >= 0; i--) {
				newFirst[i] = first[i];
			}
			if (first) {
				delete [] first;
			}
			this->first = first = newFirst;
		}
		(first[len++] = c)->retain();
		this->lockCount++;
		try {
			this->owner->addedControl(c);
		} catch (...) {
			this->lockCount--;
			throw;
		}
		this->lockCount--;
	}
}

void Control::ControlCollection::remove(Control *c) {
	if (this->lockCount) {
		_throw(IllegalStateException, "The collection is locked, don't modify it.");
	}
	if (c && this->len) {
		Control **first = this->first;
		int len = this->len;
		bool matched = false;
		if (len == 1) {
			if (first[0] == c) {
				this->first = nullptr;
				this->capacity = 0;
				this->len = 0;
				c->release();
			}
		} else if (((len - 1) << 1) > this->capacity) {
			for (int i = 0; i < len; i++) {
				if (first[i] == c) {
					for (int ii = i; ii < len - 1; ii++) {
						first[ii] = first[ii + 1];
					}
					matched = true;
					break;
				}
			}
		} else {
			Control **newFirst = new Control*[this->capacity >>= 1];
			int index = 0;
			for (int i = 0; i < this->len; i++) {
				if (first[i] == c) {
					matched = true;
				} else {
					newFirst[index++] = first[i];
				}
			}
			delete [] first;
			this->first = first = newFirst;
		}
		if (matched) {
			this->len--;
			this->lockCount++;
			try {
				this->owner->removedControl(c);
				c->release();
			} catch (...) {
				this->lockCount--;
				throw;
			}
			this->lockCount--;
		}
	}
}

int Control::ControlCollection::count() const {
	return this->len;
}

Control *Control::ControlCollection::operator[](int index) const {
	if (index < 0 || index >= this->len) {
		_throw(ArgumentException, "index out of bound");
	}
	return this->first[index];
}